/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.impl;

import java.awt.geom.Point2D;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import who.tmap.bo.BaseMap;
import who.tmap.bo.BizObjects;
import who.tmap.bo.impl.BizObjectFactory;
import who.tmap.common.TMapException;
import who.tmap.imp.dbase.DBaseFile;
import who.tmap.imp.dbase.DBaseFileReader;
import who.tmap.imp.shapefile.MainHeader;
import who.tmap.imp.shapefile.ShapeFile;
import who.tmap.imp.shapefile.impl.ShapeFileImport;
import who.tmap.imp.shapefile.shapes.Point;
import who.tmap.imp.shapefile.shapes.Polygon;
import who.tmap.imp.shapefile.shapes.Shape;
import who.tmap.toolkit.MappingToolKit;
import who.tmap.toolkit.shape.LabelShape;
import who.tmap.toolkit.shape.MapShape;
import who.tmap.toolkit.shape.MapShapes;
import who.tmap.toolkit.shape.PolygonShape;
import who.tmap.toolkit.shape.impl.MapShapeFactory;

/**
 * A helper to display a shape file using mtk
 * Only polygons are supported for the moment
 * @author stl
 */
public class ShapeFileToBaseMapHelper {
    
    private static Logger logger = Logger.getLogger(ShapeFileToBaseMapHelper.class);
    
    private void updateGeographicBox( MappingToolKit mtk, MainHeader mainHeader ) {
        
        if ( mainHeader.getXMin() < mtk.getMinX() || mtk.getMinX() == 0 ) {
            mtk.setMinX( mainHeader.getXMin() );
        }
    
	    if ( mainHeader.getYMin() < mtk.getMinY() || mtk.getMinY() == 0 ) {
	        mtk.setMinY( mainHeader.getYMin() );
	    }
	           
	    if ( mainHeader.getXMax() > mtk.getMaxX() || mtk.getMaxX() == 0 ) {
	        mtk.setMaxX( mainHeader.getXMax() );
	    }
	    
	    if ( mainHeader.getYMax() > mtk.getMaxY() || mtk.getMaxY() == 0 ) {
	        mtk.setMaxY( mainHeader.getYMax() );
	    }    
	    
    }
    
    private List<MapShape> convertPolygons( ShapeFile shapeFile, DBaseFile dbaseFile, int dbasePolygonIdx ) throws TMapException {
    	List<MapShape> shapes =new ArrayList<MapShape>();
    	
    	MapShapeFactory msf =MapShapeFactory.getInstance();
    	
        int parts, prevParts;
        
        for( int shapeCt =0; shapeCt < shapeFile.getShapes().length; shapeCt++ ) {
            Shape shape =shapeFile.getShapes()[ shapeCt ];
            
            if ( shape instanceof Polygon ) {
                Polygon polygon =(Polygon)shape;
                Point[] points =polygon.getPoints();
                               
                prevParts =0;
                
                for( int partsCt =0; partsCt < polygon.getParts().length; partsCt++ ) {                   
                    if ( partsCt == polygon.getParts().length - 1 ) {
                        parts =points.length;
                    } else {
                        parts =polygon.getParts()[ partsCt + 1 ];
                    }
                    
                    PolygonShape polyg =(PolygonShape)msf.getGraphic( MapShapes.POLYGON );
                    shapes.add( polyg );
                    
        			LabelShape labelShape =(LabelShape)msf.getGraphic( MapShapes.LABEL );
        			labelShape.setMapShape( polyg );
        			//shapes.add( labelShape );
        			
        			polyg.setLabelShape( labelShape );
                    
                    // parts-1 because we close the polygon 
                    // so we don't need to draw a segment to the 1st coordinates 
                    // as in shape file
                    for( int pointsCt =prevParts; pointsCt < parts-1; pointsCt++ ) {
                        Point2D pt2 =new Point2D.Double( points[ pointsCt ].getX(), 
                        							 	-points[ pointsCt ].getY() );
                        
                        polyg.addPoint( pt2 );
                    }
                    
                    polyg.close();
                    
                    if ( dbaseFile != null ) {
                    	polyg.setName( getNameFromAssociatedDbaseFile( dbaseFile, dbasePolygonIdx, shapeCt ) );
                    	labelShape.setText( polyg.getName() );
                    	
                    	logger.info( polyg.getName() );
                    }
                    
                    prevParts =parts;
                    
                }
            }
        }    
    	
    	return shapes;
    }
    
    public BaseMap convert( String shapeFileName, int dbasePolygonIdx ) throws TMapException {
    	BaseMap baseMap =null;
    	
    	try {
		    File file =new File( shapeFileName );
	        ShapeFile shapeFile =new ShapeFileImport().readShapeFile( file );    	
	    	
	    	DBaseFileReader dbfr =new DBaseFileReader();

        	File dbaseFilename =dbfr.convertShapeFileNameToDBaseFileName( file );
        	DBaseFile dbaseFile =dbfr.readDBaseFile( dbaseFilename );	    	
	    	
	    	MainHeader mainHeader =shapeFile.getMainHeader();
	    	
	    	baseMap =(BaseMap)BizObjectFactory.getInstance().get( BizObjects.BASE_MAP );
	    	
	    	baseMap.setMinX( mainHeader.getXMin() );
	    	baseMap.setMinY( mainHeader.getYMin() );
	    	baseMap.setMaxX( mainHeader.getXMax() );
	    	baseMap.setMaxY( mainHeader.getYMax() );    	
	    	
	    	baseMap.setShapes( convertPolygons( shapeFile, dbaseFile, dbasePolygonIdx ) );
	    	
	    	baseMap.setDbaseJoinIdx( dbasePolygonIdx );
	    	
    	} catch( Exception e ) {
    		throw new TMapException( e );
    	}
    	
    	return baseMap;
    }
        
    /**
     * Import a shapefile as mapping tool kit shapes
     * Only polygons are supported for the moment
     * @param mtk
     * @param shapeFile
     * @throws TMapException
     */
    public void convert( MappingToolKit mtk, ShapeFile shapeFile, DBaseFile dbaseFile, int dbasePolygonIdx ) throws TMapException {
        
        MainHeader mainHeader =shapeFile.getMainHeader();
              
        updateGeographicBox( mtk, mainHeader );      
        
        List<MapShape> mapShapes =convertPolygons( shapeFile, dbaseFile, dbasePolygonIdx );
        mtk.addShapes( mapShapes );
        
        mtk.setDbaseJoinIdx( dbasePolygonIdx );

        /*int parts, prevParts;
        
        for( int shapeCt =0; shapeCt < shapeFile.getShapes().length; shapeCt++ ) {
            Shape shape =shapeFile.getShapes()[ shapeCt ];
            
            if ( shape instanceof Polygon ) {
                Polygon polygon =(Polygon)shape;
                Point[] points =polygon.getPoints();
                               
                prevParts =0;
                
                for( int partsCt =0; partsCt < polygon.getParts().length; partsCt++ ) {                   
                    if ( partsCt == polygon.getParts().length - 1 ) {
                        parts =points.length;
                    } else {
                        parts =polygon.getParts()[ partsCt + 1 ];
                    }
                    
                    PolygonShape polyg =(PolygonShape)mtk.addShape( MapShapes.POLYGON );
                    
                    // parts-1 because we close the polygon 
                    // so we don't need to draw a segment to the 1st coordinates 
                    // as in shape file
                    for( int pointsCt =prevParts; pointsCt < parts-1; pointsCt++ ) {
                        Point2D pt2 =new Point2D.Double( points[ pointsCt ].getX(), 
                        							 	-points[ pointsCt ].getY() );
                        
                        polyg.addPoint( pt2 );
                    }
                    
                    polyg.close();
                    
                    if ( dbaseFile != null ) {
                    	polyg.setName( getNameFromAssociatedDbaseFile( dbaseFile, dbasePolygonIdx, shapeCt ) );
                    }
                    
                    prevParts =parts;
                    
                }
            }
        }*/      
        
        // Process display factor based on shape file import
        mtk.processMapFactor();

    }    
    
    /**
     * Try to get the first field as the polygon name
     * @param dbaseFile
     * @param idx
     * @return
     */
    private String getNameFromAssociatedDbaseFile( DBaseFile dbaseFile, int dbasePolygonIdx, int idx ) {
    	String name ="";
    	
        if ( dbaseFile != null ) {
        	try {
        		Object[] records =dbaseFile.getRecords();
        		Object[] record =(Object[])records[ idx ];
        		name =dbasePolygonIdx > -1 ? (String)record[ dbasePolygonIdx ]
        		                           : "";
        	} catch( Exception e ) {
        		
        	}
        }

        return name;
    }

}
